
#include "ImageStuff.hpp"
#include "Common.hpp"

// == Image Utilities ===========================================================================================================

#if 0
// Non portable code, switched to TGA on 18 Sep 2012
static_image_t * LoadImageFromFile(const char * filename)
{
	FILE * fp = NULL;
	static_image_t * pImage = NULL;
	BITMAPFILEHEADER bitmapFileHeader; // defined in windows.h
	BITMAPINFOHEADER bitmapInfoHeader; // defined in windows.h
	unsigned int bytesPerLine = 0, padding = 0, bmp_stride = 0;

	if ((fp = fopen(filename, "rb")) == NULL)
		goto error_condition;

	if ((pImage = (static_image_t *)calloc(1, sizeof(static_image_t))) == NULL)
		goto error_condition;

	if (fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, fp) == 0)
		goto error_condition;

	if (memcmp(&bitmapFileHeader.bfType, "BM", 2) != 0)
		goto error_condition;

	if (fread(&bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, fp) == 0)
		goto error_condition;

	// Fill The Image Info:
	pImage->width  = bitmapInfoHeader.biWidth;
	pImage->height = bitmapInfoHeader.biHeight;
	pImage->channels = (bitmapInfoHeader.biBitCount / 8);
	pImage->buffsiz  = (pImage->width * pImage->height * pImage->channels);

	if ((pImage->pixels = malloc(pImage->buffsiz)) == NULL)
		goto error_condition;

	fseek(fp, bitmapFileHeader.bfOffBits, SEEK_SET);

	bmp_stride = (pImage->width * pImage->channels);

	while ((bmp_stride % 4) != 0)
		bmp_stride++;

	bytesPerLine = (pImage->width * pImage->channels);
	padding = (bmp_stride - bytesPerLine);

	for (unsigned int scanlines = 0; scanlines < pImage->height; scanlines++)
	{
		unsigned char * LinePtr = ((unsigned char *)pImage->pixels + (bmp_stride * scanlines));

		fread(LinePtr, bytesPerLine, 1, fp);

		fseek(fp, padding, SEEK_CUR);
	}

	fclose(fp);

	return (pImage);

error_condition: /* Bad Return */

	if (fp != NULL)
		fclose(fp);

	FreeImage(&pImage);

	return (NULL);
}
#endif

static_image_t * LoadImageFromFile(const char * fileName)
{
	FILE * fp = NULL;
	int t;
	short int depth = 0;
	short int w = 0, h = 0;
	unsigned char * data = NULL;

	// Open the file in binary mode.
	fp = fopen(fileName, "rb");

	// Problem opening file?
	if (fp == NULL)
	{
		LOG_ERROR("Problem opening TGA file: " << fileName);
		return (NULL);
	}
	else
	{
		static_image_t * pImage = (static_image_t *)calloc(1, sizeof(static_image_t));
		assert(pImage != NULL);

		// Load information about the tga, aka the header:

		// Seek to the width.
		fseek(fp, 12, SEEK_SET);
		fread(&w, sizeof(short int), 1, fp);

		// Seek to the height.
		fseek(fp, 14, SEEK_SET);
		fread(&h, sizeof(short int), 1, fp);

		// Seek to the depth.
		fseek(fp, 16, SEEK_SET);
		fread(&depth, sizeof(unsigned char), 1, fp);

		// Load the actual image data:

		// Total bytes = h * w * components per pixel.
		t = h * w * (depth / 8);

		// Allocate memory for the image data.
		data = (unsigned char *)malloc(sizeof(unsigned char) * t);
		assert(data != NULL);

		// Seek to the image data.
		fseek(fp, 18, SEEK_SET);
		fread(data, sizeof(unsigned char), t, fp);

		// We're done reading.
		fclose(fp);

		LOG_MSG("Loaded TGA image: " << fileName);

		// Fill The Image Info:
		pImage->width  = w;
		pImage->height = h;
		pImage->channels = (depth / 8);
		pImage->buffsiz  = (pImage->width * pImage->height * pImage->channels);
		pImage->pixels = data;

		return (pImage);
	}
}

void FreeImage(static_image_t ** ppImage)
{
	if (*ppImage)
	{
		if ((*ppImage)->pixels)
		{
			free((*ppImage)->pixels);
		}

		free(*ppImage);

		(*ppImage) = NULL;
	}
}

static_image_t * ScaleImage(const static_image_t * pImageIn, int newWidth, int newHeight)
{
	static_image_t * pImageOut = (static_image_t *)malloc(sizeof(static_image_t));

	if (pImageOut != NULL)
	{
		pImageOut->width = newWidth;
		pImageOut->height = newHeight;
		pImageOut->channels = pImageIn->channels;
		pImageOut->buffsiz = (pImageOut->width * pImageOut->height * pImageOut->channels);

		if ((pImageOut->pixels = malloc(pImageOut->buffsiz)) == NULL)
		{
			FreeImage(&pImageOut);
			return (NULL);
		}

		// Scaling a bitmap is not actually that hard, but I'll let GLUT do the work for me today...
		if (gluScaleImage((pImageIn->channels == 4) ? (GL_BGRA_EXT) : (GL_BGR_EXT), pImageIn->width, pImageIn->height,
		GL_UNSIGNED_BYTE, pImageIn->pixels, pImageOut->width, pImageOut->height, GL_UNSIGNED_BYTE, pImageOut->pixels) != 0)
		{
			FreeImage(&pImageOut);
			return (NULL);
		}
	}

	return (pImageOut);
}

void DrawImage(int x, int y, const static_image_t * pImage)
{
	glRasterPos2i(x, y);
	glDrawPixels(pImage->width, pImage->height, (pImage->channels == 4) ? (GL_BGRA_EXT) : (GL_BGR_EXT), GL_UNSIGNED_BYTE, pImage->pixels);
}

// == Class Sprite2D ============================================================================================================

float Sprite2D::m_elapsed_time(0.0f);
double Sprite2D::m_time[2];

Sprite2D::Sprite2D(bool free_images_on_death) : m_image_frames(), kill_ptrs(free_images_on_death), m_current_frame(0), m_time_count(0.0f)
{
}

void Sprite2D::PushFrame(const static_image_t * pImage, float delay)
{
	m_image_frames.push_back(Sprite2D::frame_t(pImage, delay));
}

void Sprite2D::Draw(int x, int y, unsigned int firstFrame, unsigned int lastFrame)
{
	frame_deque::const_reference frame = m_image_frames.at(m_current_frame);

	DrawImage(x, y, frame.image_pointer);

	m_time_count += m_elapsed_time;

	if (m_time_count > frame.delay)
	{
		++m_current_frame;

		if (m_current_frame > lastFrame)
			m_current_frame = firstFrame;

		m_time_count = 0.0f;
	}
}

void Sprite2D::Draw(int x, int y)
{
	frame_deque::const_reference frame = m_image_frames.at(m_current_frame);

	DrawImage(x, y, frame.image_pointer);

	m_time_count += m_elapsed_time;

	if (m_time_count > frame.delay)
	{
		++m_current_frame;

		if (m_current_frame >= m_image_frames.size())
			m_current_frame = 0;

		m_time_count = 0.0f;
	}
}

void Sprite2D::UpdateAnimationTimer(void)
{
	m_time[0] = glutGet(GLUT_ELAPSED_TIME);
	m_elapsed_time = static_cast<float>((m_time[0] - m_time[1]) * 0.001);
	m_time[1] = m_time[0];
}

const static_image_t * Sprite2D::GetFrame(unsigned int frame) const
{
	return (m_image_frames.at(frame).image_pointer);
}

unsigned int Sprite2D::CurrentFrame(void) const
{
	return (m_current_frame);
}

unsigned int Sprite2D::NumFrames(void) const
{
	return (m_image_frames.size());
}

void Sprite2D::SetPerFrameDelay(float delay)
{
	Sprite2D::frame_deque::iterator _Ptr = m_image_frames.begin();
	Sprite2D::frame_deque::const_iterator _End = m_image_frames.end();

	while (_Ptr != _End)
	{
		(*_Ptr).delay = delay;
		++_Ptr;
	}
}

void Sprite2D::FreeImagePointers(void)
{
	if (!m_image_frames.empty())
	{
		Sprite2D::frame_deque::iterator _Ptr = m_image_frames.begin();
		Sprite2D::frame_deque::const_iterator _End = m_image_frames.end();

		while (_Ptr != _End)
		{
			FreeImage(const_cast<static_image_t **>(&(*_Ptr).image_pointer));
			++_Ptr;
		}

		m_image_frames.clear();
	}
}

Sprite2D::~Sprite2D(void)
{
	if (kill_ptrs)
	{
		FreeImagePointers();
	}
}

// == glPrintf(...) =============================================================================================================

int glPrintf(int x, int y, const char * format, ...)
{
	char buffer[2048];
	va_list arg_list;
	int iCount;

	va_start(arg_list, format);
	iCount = vsnprintf(buffer, sizeof(buffer), format, arg_list);
	va_end(arg_list);

	glRasterPos2i(x, y);

	for (int i = 0; i < iCount; i++)
		glutBitmapCharacter(GLUT_BITMAP_8_BY_13, buffer[i]);

	return (iCount);
}

// == Class Button2D ===========================================================================================================

Button2D::Button2D(void)
{
	image = 0;
	x = 0;
	y = 0;
}

Button2D::Button2D(static_image_t *_image, int _x, int _y)
{
	image = _image;
	x = _x;
	y = _y;
}

Button2D::~Button2D(void)
{
}

void Button2D::FreeMemory(void)
{
	FreeImage(&image);
}

void Button2D::SetImage(static_image_t *_image, int _x, int _y)
{
	image = _image;
	x = _x;
	y = _y;
}

void Button2D::DrawButton(void) const
{
	DrawImage(x, y, image);
}

bool Button2D::testColision(int _x, int _y) const
{
	return ((_x > x) && (_x < (int)(x + image->width)) && (_y > (int)(y - image->height)) && (_y < y));
}

static_image_t * Button2D::returnImage(void) const
{
	return (image);
}
